(function( $, undefined ) {

/*
	\name ru Таблица
	\description ru Таблица с опциональной возможностью редактирования
	\is_container 0
	\save true
*/
//***********************************************************
$.widget("ui.ajajform_grid",$.ui.ajajform_widget, {
	options: {
		/* \type cssclass */
		/* \name ru CSS-класс */
		/* \description ru CSS-класс окружающего DIV */
		cssclass: "afw-grid",
		/* \type enum */
		/* \enum arguments */
		/* \enum json */
		post: "arguments",
		rowClasses: [],
		/* \type html */
		/* \name ru Шаблон виджета */
		/* \description ru HTML-шаблон виджета */
		htmltemplate: '<div class="af-title"></div><div class="scroll-x"><div class="af-colheaders"></div><div class="af-gridbody"></div></div><div class="af-footer"></div>',
		/* \type html */
		htmltemplatecolheader: '<div class="af-colheader"></div>',
		/* \type html */
		htmltemplaterow: '<div class="af-row"></div>',
		/* \type integer */
		defaultCellWidth: 90,
		rows: [],
		/* \type enum */
		/* \name ru Режим таблицы */
		// \description ru Таблица может отображаться несколькими вариантами, с возможностью редактирования или без неё, а также с разными способами отсылки изменённых данных.
		// \description ru * view - отображение таблицы
		// \description ru * form-edit-full - таблица с возможностью редактирования и с сохранением данных через AJAJ-интерфейс формы
		// \description ru * standalone-popup-edit-on-click - в разработке
		// \description ru * standalone-edit-on-click - в разработке
		// \description ru * standalone-edit-full - в разработке
		// \enum view
		// \enum form-edit-full
		mode: "view",
		// \type text
		title: ""
	},
	//***********************************************************
	// Создает widget
	//***********************************************************
	_create: function() {
		this.element.addClass(this.options.cssclass);
		this.element.append(this.options.htmltemplate);
		this.refreshTitle();
		this.refreshClasses();
		this.refreshRows();
	},
	//***********************************************************
	// Используется при изменении классов строк
	//***********************************************************
	refreshClasses: function() {
		this.classesByName={};
		this.sortable=false;
		var cls=this.options.rowClasses;
		if (!cls.length) {this.refreshColHeaders();return;}
		for (var i=0;i<cls.length;i++) {
			this.classesByName[cls[i].name]=cls[i];
			if (cls[i].sortable) this.sortable=true;
			for (var j=0;j<cls[i].columns.length;j++) {
				if (cls[i].columns[j].source) {
					var si=cls[i].columns[j].sourceById={};
					var s=cls[i].columns[j].source;
					for (var k=0;k!=s.length;k++) {
						si[s[k].id]=s[k].name;
					}
				}
			}
		}
		this.refreshColHeaders();
	},
	//***********************************************************
	// Изменяет заголовки таблицы
	// Вызываается при изменении классов строк
	//***********************************************************
	refreshColHeaders: function() {
		$(".af-colheaders",this.element).empty();
		if (this.options.rowClasses.length==0) return;
		var h=this.options.rowClasses[0];
		var headers=$(".af-colheaders",this.element);
		this.columnWidthes=[];
		var ww=0;
		var wc=0;
		if (this.sortable) {
			var ce=$(this.options.htmltemplatecolheader);
			ce.width(30);
			ce.appendTo(headers);
			ww+=30;
			wc++;
		}
		for (var i=0;i<h.columns.length;i++) {
			var c=h.columns[i];
			var ce=$(this.options.htmltemplatecolheader);
			ce.text(c.label||c.name);
			var w=c.width||this.options.defaultCellWidth;
			this.columnWidthes[i]=w;
			ce.width(w);
			ce.appendTo(headers);
			var ar="#"+this.element.prop("id")+' .af-row > .af-cell:nth-child('+(i+1+(this.sortable?1:0))+')';
			ce.resizable({
				handles: "e",
				minWidth: 20,
				alsoResize: ar,
				stop: function(t,colnum) {
						return function(e,ui) {t.columnWidthes[colnum]=ui.size.width;}
					}(this,i)
			});
			ww+=w;
			wc++;
		}
		if (this.options.rowSelection) {
			var ce=$(this.options.htmltemplatecolheader);
			ce.width(30);
			ce.appendTo(headers);
			ww+=30;
			wc++;
		}
		ww+=wc*10;
		ww+=30;
		if ($(".af-title",this.element).width()<ww) {
			$(".af-colheaders",this.element).width(ww);
			$(".af-gridbody",this.element).width(ww);
		} else {
			$(".af-colheaders",this.element).width("100%");
			$(".af-gridbody",this.element).width("100%");
		}
	},
	//***********************************************************
	// Вызывается при изменении заголовка
	//***********************************************************
	refreshTitle: function() {
		$(".af-title",this.element).text(this.options.title);
	},
	//***********************************************************
	// Показывает или прячет тело таблицы,
	// используется для того чтобы не показывать таблицу при заполнении
	//***********************************************************
	showBody: function(a) {
		if (a) $(".af-gridbody",this.element).show(); else $(".af-gridbody",this.element).hide();
	},
	//***********************************************************
	// Вычисляет флаг редактируемости "по-умолчанию"
	//***********************************************************
	defaultEditFlag: function() {
		return (this.options.mode=="standalone-edit-full" || this.options.mode=="form-edit-full")?true:false;
	},
	refreshMode: function() {
		this.refreshRows();
	},
	//***********************************************************
	// Перерисовывает строчки
	//***********************************************************
	refreshRows: function() {
		this.showBody(false);
		$(".af-gridbody",this.element).empty();
		var editFlag=this.defaultEditFlag();
		for (var i=0;i<this.options.rows.length;i++) {
			this.renderRow(i,editFlag);
		}
		this.showBody(true);
		this.setSortable();
	},
	//***********************************************************
	// 
	//***********************************************************
	setSortable: function() {
		if (!this.sortable) return;
		var t=this;
		$(".af-gridbody",this.element).sortable({
			handle: ".af-cell-sortable-on",
			stop: function(event,ui) {
				var r={
					ordering:$(this).sortable("toArray").join(',')
				};
				for (var k in t.options.sort.params) r[k]=t.options.sort.params[k];
				std_response(
					$.ajax({
						type: "POST",
						url: t.options.sort.url,
						data: r,
						async: false
					}).responseText
				);
			}
		});
	},
	//***********************************************************
	// Добавляет строку
	//***********************************************************
	addRow: function(row) {
		this.options.rows.push(row);
		this.showBody(false);
		var editFlag=this.defaultEditFlag();
		this.renderRow(this.options.rows.length-1,editFlag);
		this.showBody(true);
	},
	//***********************************************************
	// 
	//***********************************************************
	genId: function() {
		var id=0;
		var rr=this.options.rows;
		for (var i=0;i<rr.length;i++) {
			if (typeof rr[i].id=="number") continue;
			var a=rr[i].id.match(/^new(\d+)/);
			if (a) if (id<a[1]*1) id=a[1]*1;
		}
		return "new"+(id+1);
	},
	//***********************************************************
	// Перерисовывает одну строку
	// TODO косячит
	//***********************************************************
	renderRow: function(rownum,editFlag) {
		var row=this.options.rows[rownum];
		if (!row) return;
		var r=$(".af-gridbody > .af-row:nth-child("+(rownum+1)+")",this.element);
		while (r.length==0) {
			r=$(this.options.htmltemplaterow).appendTo($(".af-gridbody",this.element));
		}
		var prefix=this.options.name+"-"+(row.id||"new"+rownum);
		r.prop("id",prefix);
		var cl=this.classesByName[row.rowClass];
		if (!cl) return;
		r.empty();
		// TODO косяк
		if (row.readOnly || cl.readOnly) editFlag=false;
		if (!cl) return;
		if (row.cssclass) r.addClass(row.cssclass);
		else if (cl.cssclass) r.addClass(cl.cssclass);
		if (this.sortable) {
			if (cl.sortable) {
				ce=$('<div class="af-cell af-cell-sortable-on"></div>');
			} else {
				ce=$('<div class="af-cell af-cell-sortable-off"></div>');
			}
			r.append(ce);
		}
		for (var i=0;i<cl.columns.length;i++) {
			var column=cl.columns[i];
			var cell=row.cells.length>i?row.cells[i]:"";
			ce=$('<div class="af-cell"></div>');
			ce.width(this.columnWidthes[i]);
			if (column.cssclass) ce.addClass(column.cssclass);
			if (i==0 && row.level) ce.css("margin-left",20*row.level).width(this.columnWidthes[i]-20*row.level);
			this.renderCell(ce,cell,column,row,editFlag,rownum);
			r.append(ce);
		}
		if (this.options.rowSelection) {
			ce=$('<div class="af-cell-selector"></div>');
			$('<input type="checkbox" id="'+this.options.name+'-selector" value="'+rownum+'"/>').appendTo(ce);
			r.append(ce);

		}
	},
	//***********************************************************
	// Заполняет ячейку
	//***********************************************************
	renderCell: function(div,celldata,col,row,editflag,rownum) {
		if (col.type=="none") return;
		if (editflag && col.readOnly) editflag=false;
		var a='renderCell_'+(col.type||'text')+'_';
		var b='renderCell_text_';
		if (editflag) {a+='edit';b+='edit';} else {a+='view';b+='view';}
		if (this[a]) this[a](div,celldata,col,row,rownum); else this[b](div,celldata,col,row,rownum);
		if (col.click)
			div.bind("click",{t: this, rownum: rownum, col: col,row: row},
				function(e) { col.click(e.data.t,e.data.rownum,e.data.col,e.data.row);}
			);
	},
	//***********************************************************
	// Добавляет строку
	//***********************************************************
	insertRow: function(before,row) {
		var editFlag=this.defaultEditFlag();
		if (before!=null && this.options.length>before) {
			this.saveChangedRows();
			this.options.rows.splice(before,0,row);
			this.refreshRows();
		} else {
			this.options.rows.push(row);
			this.renderRow(this.options.rows.length-1,editFlag);
		}
	},
	//***********************************************************
	// Проверяет все ячейки таблицы с помощью validate
	//***********************************************************
	validate: function() {
		return false;
	},
	//***********************************************************
	// Проверяет одну заданную ячейку
	//***********************************************************
	validateCell: function(inp,vd) {
		if (!vd) return false;
		var r=vd(inp.val());
		if (r) inp.addClass("ui-state-error"); else inp.removeClass("ui-state-error");
		return r?true:false;
	},
	//***********************************************************
	// Формирует данные для формы
	//***********************************************************
	postvalue: function(r) {
		if (this.options.mode!="form-edit-full") return;
		this.saveChangedRows();
		var levelIds=[];
		var rr;
		if (this.options.post=="arguments") rr=r; else rr={};
		for (var rownum=0;rownum<this.options.rows.length;rownum++) {
			this.postvalueRow(rr,rownum,levelIds,this.options.post=="json"?true:false);
		}
		if (this.options.post=="json") {
			if (!this.options.postAllFunction) r[this.options.name]=JSON.stringify(rr); else r[this.options.name]=JSON.stringify(this.options.postAllFunction(this,rr));
		}
	},
	//***********************************************************
	// Формирует данные для формы, конкретной строки
	//***********************************************************
	postvalueRow: function(r,rownum,levelIds,noName) {
		var row=this.options.rows[rownum];
		if (row.readOnly) return;
		var cl=this.classesByName[row.rowClass];
		var id=row.id||"new"+rownum;
		var prefix=(noName?"":this.options.name+"-")+id+"-";
		r[prefix+"rowClass"]=row.rowClass;
		levelIds[row.level||0]=id;
		if (row.level && row.level>0) r[prefix+"parent_id"]=levelIds[row.level-1];
		if (cl.readOnly) return;
		for (var cellnum=0;cellnum<cl.columns.length;cellnum++) {
			var col=cl.columns[cellnum];
			r[prefix+col.name]=row.cells[cellnum];
		}
	},
	
	//***********************************************************
	// Cохраняет данные из редактируемых
	// строк в массив rows
	//***********************************************************
	saveChangedRows:function() {
		for (var i=0;i<this.options.rows.length;i++) {
			var row=this.options.rows[i];
			if (row.readOnly) continue;
			var rowclass=this.classesByName[row.rowClass];
			if (rowclass.readOnly) continue;
			var cells=row.cells;
			for (var j=0;j<rowclass.columns.length;j++) {
				if (rowclass.columns[j].readOnly) continue;
				cells[j]=this.cellHtmlValue(i,j);
			}
		}
	},
	
	//***********************************************************
	// 
	//***********************************************************
	getRow: function(rownum) {
		return this.options.rows[rownum];
	},

	//***********************************************************
	// Возвращает значение редактируемой ячейки, берёт из HTML
	//***********************************************************
	cellHtmlValue: function(rownum,cellnum) {
		var row=this.options.rows[rownum];
		var cl=this.classesByName[row.rowClass];
		var col=cl.columns[cellnum];
		if (!col) return undefined;
		if (col.type=="none") return undefined;
		if (col.readOnly) return undefined;
		var fetcher='renderCell_'+col.type+'_fetch';
		if (!this[fetcher]) fetcher='renderCell_text_fetch';
		return this[fetcher](rownum,cellnum);
	},
	//***********************************************************
	// Возвращает список выбранных строк
	//***********************************************************
	selectedRownums: function() {
		var ret=new Array();
		$('input#'+this.options.name+'-selector:checked',this.element).each(function() {ret.push(this.value);} );
		return ret;
	},
	//***********************************************************
	// Вызывает функцию для выбранных строк
	//***********************************************************
	applyToSelectedRownums: function(funcname) {
		var ret=this.selectedRownums();
		for (var i=0;i<ret.length;i++) {
			funcname(this,i);
		}
	},
	//***********************************************************
	// Удаляет выбранные строки
	//***********************************************************
	deleteSelectedRownums: function() {
		this.saveChangedRows();
		var ret=this.selectedRownums();
		var s={};
		for (var i=0;i<ret.length;i++) {
			s[ret[i]]=1;
		}
		var a=new Array();
		for (var i=0;i<this.options.rows.length;i++) {
			if (!s[i]) a.push(this.options.rows[i]);
		}
		this.options.rows=a;
		this.refreshRows();
	},
	
	//***********************************************************
	// row,col,rowClass - optional
	//***********************************************************
	isCellEditable: function(rownum,colnum,row,col,rowClass) {
		if (!row) row=this.options.rows[rownum];
		if (!rowClass) rowClass=this.classesByName[row.rowClass];
		if (!col) col=rowClass.columns[colnum];
		var editFlag=this.defaultEditFlag();
		if (row.readOnly) editFlag=false;
		if (rowClass.readOnly) editFlag=false;
		if (col.readDonly) editFlag=false;
		return editFlag;
	},
	//***********************************************************
	// 
	//***********************************************************
	getCellDiv: function(rownum,colnum,row,col,rowClass) {
		if (!row) row=this.options.rows[rownum];
		if (!rowClass) rowClass=this.classesByName[row.rowClass];
		if (!col) col=rowClass.columns[colnum];
		return $(".af-gridbody > .af-row:nth-child("+(rownum+1)+") > .af-cell:nth-child("+(((this.sortable && rowClass.sortable)?1:0)+colnum+1)+")",this.element);
	},
	//***********************************************************
	// Редактирует клетку
	//***********************************************************
	editCell: function(rownum,colnum,value) {
		if (this.options.rows.length<=rownum) return;
		var row=this.options.rows[rownum];
		var rowClass=this.classesByName[row.rowClass];
		var col=rowClass.columns[colnum];
		
		row.cells[colnum]=value;
		var editflag=this.isCellEditable(rownum,colnum,row,col,rowClass);
		var div=this.getCellDiv(rownum,colnum,row,col,rowClass);
		this.renderCell(div,value,col,row,editflag,rownum);
	},

/*****************************************************************************/
	renderCell_integer_view: function(div,celldata,col,row) {
		div.addClass("af-right");
		this.renderCell_text_view(div,celldata,col,row);
	},
	renderCell_integer_edit: function(div,celldata,col,row) {
		div.removeClass("af-right");
		this.renderCell_text_edit(div,celldata,col,row);
	},
	renderCell_number_view: function(div,celldata,col,row) {
		div.addClass("af-right");
		this.renderCell_text_view(div,celldata,col,row);
	},
	renderCell_number_edit: function(div,celldata,col,row) {
		div.removeClass("af-right");
		this.renderCell_text_edit(div,celldata,col,row);
	},

	renderCell_date_edit: function(div,celldata,col,row) {
		var i=this.renderCell_text_edit(div,celldata,col,row);
		i.datepicker({firstDay:1,dateFormat: 'dd.mm.yy'});
	},
	
	renderCell_text_view: function(div,celldata,col,row) {
		if (col.html || col.url) {
			var r="";
			if (col.url) r+='<a href="'+col.url+row.id+'">';
			r+=celldata;
			if (col.url) r+='</a>';
			div.html(r);
		} else {
			div.text(celldata);
		}
	},
	renderCell_text_edit: function(div,celldata,col,row) {
		div.empty();
		var i=$('<input type="text" style="width: 100%;"/>').val(celldata||"").appendTo(div);
		if (col.autocomplete) i.autocomplete(col.autocomplete);
		i.bind("focus",function(e) { $(this).addClass("ui-state-highlight"); });
		if (col.validate) {
			i.bind("blur",{t:this,i:i,f:col.validate},function(e) {
				$(this).removeClass("ui-state-highlight");
				e.data.t.validateCell(e.data.i,e.data.f);
			});
		} else {
			i.bind("blur",function(e) { $(this).removeClass("ui-state-highlight"); });
		}
		return i;
	},
	renderCell_text_fetch: function(rownum,colnum) {
		return $(".af-row:nth-child("+(rownum+1)+") > .af-cell:nth-child("+(colnum+1)+") > input",this.element).val();
	},
	
	renderCell_checkbox_view: function(div,celldata,col,row) {
		if (celldata=="0") celldata=false;
		div.removeClass(celldata?"af-checkbox":"af-checkboxA");
		div.addClass(celldata?"af-checkboxA":"af-checkbox");
	},
	renderCell_checkbox_edit: function(div,celldata,col,row) {
		if (celldata=="0") celldata=false;
		div.empty();
		div.removeClass("af-checkboxA");
		div.removeClass("af-checkbox");
		div.css("text-align","center");
		$('<input type="checkbox" value="1"/>').prop("checked",celldata?true:false).appendTo(div);
	},
	renderCell_checkbox_fetch: function(rownum,colnum) {
		i=$(".af-row:nth-child("+(rownum+1)+") > .af-cell:nth-child("+(colnum+1)+") > input",this.element);
		return i.prop("checked")?1:0;
	},

	renderCell_select_view: function(div,celldata,col,row) {
		div.text(col.sourceById[celldata]);
	},
	renderCell_select_edit: function(div,celldata,col,row,rownum) {
		var t=this;
		div.empty();
		var sel=$('<select style="width: 100%;"></select>').appendTo(div);
		var s=null;
		for (var i=0;i<col.source.length;i++) {
			$(new Option(col.source[i].name,col.source[i].id)).appendTo(sel);
			if (col.source[i].id==celldata) s=i;
		}
		if (s!=null) sel.prop("selectedIndex",s);
		if (col.change) {
			sel.bind("change",{tag: sel, grid: this, row: row, col:col/*, celldata: celldata*/, rownum: rownum},function (event) {
				event.data.celldata=sel.val();
				return col.change(event);
			});
		}
	},
	renderCell_select_fetch: function(rownum,colnum) {
		var sel=$(".af-row:nth-child("+(rownum+1)+") > .af-cell:nth-child("+(colnum+1)+") > select",this.element);
		return sel.prop("options")[sel.prop("selectedIndex")].value;
	},

	renderCell_progress_any: function(div,celldata,col,editFlag) {
		div.empty();
		if (editFlag) $('<input type="hidden">').val(celldata||0).appendTo(div);
		for (var i=0;i<col.source.length;i++) {
			var cs=col.source[i];
			var item=$('<div></div>');
			item.addClass("af-progress"+(cs.id<=celldata?1:0));
			item.prop("pr","af-progress"+(cs.id<=celldata?1:0));
			item.prop("title",cs.name);
			if (editFlag) {
				item.bind("mouseover",item,function(e) {e.data.addClass("af-progressH");});
				item.bind("mouseout",item,function(e) {e.data.removeClass("af-progressH");});
				item.bind("click",{t:this,id:cs.id,col:col,div:div},
						function(e) {
							e.data.t.renderCell_progress_any(e.data.div,e.data.id,e.data.col,true);
						});
			}
			div.append(item);
		}
	},
	renderCell_progress_view: function(div,celldata,col,row) {
		this.renderCell_progress_any(div,celldata,col,false);
	},
	renderCell_progress_edit: function(div,celldata,col,row) {
		this.renderCell_progress_any(div,celldata,col,true);
	}
});

})( jQuery );
